﻿using Microsoft.Extensions.Configuration;
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Protocol;
using Sensor_Entity;
using ServiceCore.Tool.Dapper;
using ServiceCore.Tool.ES;
using ServiceCore.Tool.MongoDB;
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Linq;

namespace ServiceCore.Tool.Mqtt
{
    public interface IEMQ_Helper
    {
        void StartListenMqtt();
        System.Threading.Tasks.Task Publish(string topic, string data, MqttQualityOfServiceLevel mqttQualityOfServiceLevel);
    }
    public class EMQ_Helper : IEMQ_Helper
    {
        private static object obj1 = new object();
        private static object obj = new object();
        private static IMqttClient _mqttClient;
        private readonly IConfiguration AppSettingConfig;
        private readonly IEs_Log_Helper _esService;
        private readonly IEmqMongo_Service _empMongoService;
        private readonly IDapper_Helper _dapperService;
        private readonly ISnowflake_Helper _snowflakeService;
        public EMQ_Helper(IConfiguration configuration, IEs_Log_Helper esService, IEmqMongo_Service empMongoService, IDapper_Helper dapperService, ISnowflake_Helper snowflakeService)
        {
            AppSettingConfig = configuration;
            _esService = esService;
            _empMongoService = empMongoService;
            _dapperService = dapperService;
            _snowflakeService = snowflakeService;
        }
        public string mqtt_url { get { return AppSettingConfig["mqtt:url"]; } }
        public string mqtt_ClientId { get { return AppSettingConfig["mqtt:clientid"]; } }
        public string mqtt_name { get { return AppSettingConfig["mqtt:name"]; } }
        public string mqtt_pwd { get { return AppSettingConfig["mqtt:pwd"]; } }
        /// <summary>
        ///  定义个mqtt连接
        /// </summary>
        public IMqttClient mqttClient
        {
            get
            {
                if (_mqttClient == null)
                {
                    lock (obj)
                    {
                        MqttFactory factory = new MqttFactory();
                        _mqttClient = factory.CreateMqttClient();
                        return _mqttClient;
                    }
                }
                else
                {
                    return _mqttClient;
                }
            }
        }
        /// <summary>
        /// 定义MQTT连接属性
        /// </summary>
        public IMqttClientOptions mqttOption
        {
            get
            {
                var options = new MqttClientOptionsBuilder().WithTcpServer(mqtt_url, 1883)
                                          .WithClientId($"{Guid.NewGuid().ToString("N")}@@@{mqtt_ClientId}").WithCredentials(mqtt_name, mqtt_pwd)
                                          .WithCleanSession()
                                          .Build();
                return options;
            }
        }
        /// <summary>
        /// 手动开始一次mqtt连接
        /// </summary>
        public async void Start()
        {
            var result = await mqttClient.ConnectAsync(mqttOption);
            Console.WriteLine($"启动程序启动了=>{Newtonsoft.Json.JsonConvert.SerializeObject(result)}");
        }
        /// <summary>
        /// 开始mqtt连接监听
        /// </summary>
        public async void StartListenMqtt()
        {
            MqttStart();
            while (true)
            {
                try
                {
                    if (!mqttClient.IsConnected)
                    {
                        try
                        {
                            var result = await mqttClient.ConnectAsync(mqttOption);
                            Console.WriteLine($"监听程序启动了=>{Newtonsoft.Json.JsonConvert.SerializeObject(result)}");
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.Message);
                        }
                        Thread.Sleep(3000);
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
                finally
                {
                    Thread.Sleep(1);
                }
            }
        }
        /// <summary>
        /// 订阅相关信息
        /// </summary>
        private void MqttStart()
        {
            mqttClient.Connected += (object sender, MqttClientConnectedEventArgs e) =>
            {
                mqttClient.SubscribeAsync("sensor/#");
                mqttClient.SubscribeAsync($"$SYS/brokers/rk@47.106.34.163/clients/#");
                Console.WriteLine($"连接成功");
                _esService.Add_Log("服务器连接MQTT成功", "/mqttClient");
            };
            mqttClient.Disconnected += (object sender, MqttClientDisconnectedEventArgs e) =>
            {
                Console.WriteLine($"连接断开");
                _esService.Add_Log("服务器MQTT==>连接断开", "/mqttClient");
            };
            mqttClient.ApplicationMessageReceived += (object sender, MqttApplicationMessageReceivedEventArgs e) =>
            {
                string topic = e.ApplicationMessage.Topic;
                string data = System.Text.Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
                //Console.WriteLine($"收到==>{topic}====>{data}");
                _esService.Add_Log($"{DateTime.Now}===>{topic}===>{data}", "/MessageReceived");
                try
                {
                    if (Regex.IsMatch(topic, "^(?=.*/connected).*$"))
                    {
                        try
                        {
                            connectedClass mqttCon = Newtonsoft.Json.JsonConvert.DeserializeObject<connectedClass>(data);
                            string[] info = mqttCon.clientid.Split("@@@");
                            if (info.Length != 2)
                            {
                                _esService.Add_Log($"{DateTime.Now}===>{topic}===>{data}==>未能解析clientid", "/MessageReceived/connected");
                            }
                            else
                            {
                                mqttCon.info.id = info[0];
                                mqttCon.info.sn = info[1];
                                _empMongoService.saveConnected(mqttCon);
                                int count = _dapperService.do_Execute("update product_base set is_online=@is_online,lastClientID=@lastClientID,last_online_time=@last_online_time where sn=@sn limit 1", new { is_online = true, lastClientID = info[0], last_online_time = DateTime.Now, sn = info[1] });
                                _esService.Add_Log($"{DateTime.Now}===>{mqttCon.info.sn}===>{mqttCon.info.id}==>连接=>{count}", "/MessageReceived/connected");
                            }
                        }
                        catch (Exception ex)
                        {
                            _esService.Add_Log($"{DateTime.Now}===>{topic}===>{data}==>{ex.Message}", "/MessageReceived/connected");
                        }
                    }
                    else if (Regex.IsMatch(topic, "^(?=.*/disconnected).*$"))
                    {

                        try
                        {
                            disconnectedClass mqttDisCon = Newtonsoft.Json.JsonConvert.DeserializeObject<disconnectedClass>(data);
                            string[] info = mqttDisCon.clientid.Split("@@@");
                            if (info.Length != 2)
                            {
                                _esService.Add_Log($"{DateTime.Now}===>{topic}===>{data}==>未能解析clientid", "/MessageReceived/disconnected");
                            }
                            else
                            {
                                mqttDisCon.info.id = info[0];
                                mqttDisCon.info.sn = info[1];
                                _empMongoService.saveDisConnected(mqttDisCon);
                                int count = _dapperService.do_Execute("update product_base set is_online=@is_online where sn=@sn and lastClientID=@lastClientID limit 1", new { is_online = false, lastClientID = info[0], last_online_time = DateTime.Now, sn = info[1] });
                                _esService.Add_Log($"{DateTime.Now}===>{mqttDisCon.info.sn}===>{mqttDisCon.info.id}==>断开连接=>{count}", "/MessageReceived/disconnected");
                            }
                        }
                        catch (Exception ex)
                        {
                            _esService.Add_Log($"{DateTime.Now}===>{topic}===>{data}==>{ex.Message}", "/MessageReceived/disconnected");
                        }
                    } 
                }
                catch (Exception ex)
                {
                    _esService.Add_Log($"{DateTime.Now}===>{topic}===>{data}==>{ex.Message}", "/MessageReceived");
                }
            };
        }
        private static object lockpublish = new object();
        /// <summary>
        /// 发送MQTT指令
        /// </summary>
        /// <param name="topic"></param>
        /// <param name="data"></param>
        /// <param name="mqttQualityOfServiceLevel"></param>
        /// <returns></returns>
        public System.Threading.Tasks.Task Publish(string topic, string data, MqttQualityOfServiceLevel mqttQualityOfServiceLevel)
        {

            if (mqttClient.IsConnected)
            {

                lock (lockpublish)
                {
                    Console.WriteLine($"Publish topic={topic},data={data},=");
                    return mqttClient.PublishAsync(topic, data, mqttQualityOfServiceLevel);
                }
            }
            return null;

        }
        public class connectedClass
        {
            public connectedClass() {
                this.info = new ClientInfo();
            }
            public bool clean_start { get; set; }

            public string clientid { get; set; }
            public int connack { get; set; }

            public string ipaddress { get; set; }

            public int keepalive { get; set; }

            public string proto_name { get; set; }

            public int proto_ver { get; set; }

            public long ts { get; set; }

            public string username { get; set; }

            public ClientInfo info { get; set; }

            public DateTime addtime { get; set; } = DateTime.Now;

        }
        public class disconnectedClass
        {
            public disconnectedClass()
            {
                this.info = new ClientInfo();
            }
            public string clientid { get; set; }

            public string username { get; set; }

            public string reason { get; set; }

            public long ts { get; set; }

            public ClientInfo info { get; set; }
            public DateTime addtime { get; set; } = DateTime.Now;
        }
        public class ClientInfo
        {
            public string id { get; set; }

            public string sn { get; set; }
        }
        public class subdata_model
        {
            public string sn { get; set; } 
            public string value { get; set; }
            public string head { get; set; }
        }
    }
}
